-[ c library "parse.c", header "parse.h" ]

-[public] typedef struct linenumber {
	char *file;
	int line;
	struct linenumber *prev;
} linenumber_t;

-[private] enum utoken_kind {
	tk_number, tk_strlit, tk_word,
	tk_macroarg, tk_nmacroarg, tk_smacroarg, tk_wmacroarg,
	tk_stop,
	tk_lcurly, tk_rcurly,
	tk_lsquare, tk_rsquare,
	tk_lround, tk_rround,
};

-[private] typedef struct {
	enum utoken_kind kind;
	char *data;
	linenumber_t *linenumber;
} utoken_t;

-[ utokenbuffer_t : private buffer [500] of utoken_t ]

-[private] void ulang_scan
	(char *buf,size_t len,char *fn,
	 utoken_t** tokens,size_t *ntokens)
{
	int dummy;
	utoken_t token;
	char *p;
	enum utoken_kind kind;
	char *end;
	char *marker;
	utokenbuffer_t *tb;
	int linenumber;

	tb= utokenbuffer_new();
	end= buf + len;
	p= buf;

	linenumber= 1;

restart:
	buf= p;

![ re2c "-1" "-i" "-" ]

/*!re2c
	re2c:define:YYCTYPE  = "unsigned char";
	re2c:define:YYCURSOR = p;
	re2c:define:YYLIMIT= end;
	re2c:define:YYMARKER= marker;
	re2c:yyfill:enable   = 0;
	re2c:yych:conversion = 1;

	NL= [\012];
	SPACE= [\001-\011\013-\040\177];
	Q= [\042];
	S=  [\044];
	ANY= [\001-\377];
	BACK= [\134];
	SCHAR = [^\000\042\134];
	SLASH= [/];
	CMCHAR= [\001-\011\013-\377];
	LETTER= [A-Za-z_];
	IDENT= [A-Za-z_0-9];
	PUNCT= [!%&*+\055,./:$#<=>?^\140|~\200-\377];

	NL              { linenumber++; goto restart; }
	SPACE+     { goto restart; }
	[0-9]+        {kind= tk_number; goto done;}
	Q (SCHAR | (BACK ANY) )* Q {kind= tk_strlit; goto done; }
	S "S:" LETTER IDENT* { kind= tk_smacroarg; goto done; }
	S "N:" LETTER IDENT* { kind= tk_nmacroarg; goto done; }
	S "W:" LETTER IDENT* { kind= tk_wmacroarg; goto done; }
	S LETTER IDENT* { kind= tk_macroarg; goto done; }
	SLASH SLASH CMCHAR* NL { linenumber++; goto restart; }
	"@" LETTER IDENT* { kind=tk_word; goto done; }
	LETTER IDENT* { kind= tk_word; goto done; }
	";"		{ kind= tk_stop; goto done; }
	"{"              { kind=tk_lcurly; goto done; }
	"}"              { kind= tk_rcurly; goto done; }
	"("               { kind= tk_lround; goto done; }
	")"               { kind= tk_rround; goto done; }
          "["                { kind= tk_lsquare; goto done; }
          "]"                { kind= tk_rsquare; goto done; }
	PUNCT+     { kind= tk_word; goto done; }
	"\000"         { goto eof; }
	.                  { goto lexer_error; }
*/

![end]

done:
	token.kind= kind;
	token.data= make_str(buf,p);
	token.linenumber= linenumber_new(fn, linenumber);
	utokenbuffer_append( tb, token);
	buf= p;
	goto restart;

eof:
	*tokens= utokenbuffer_collectda( tb, &dummy );
	*ntokens= dummy;
	return ;

lexer_error:
	fprintf(stderr,"%s:%d: lexer error: unrecognized "
		"character %c (%02x)\n", fn, linenumber,
		*p, * (unsigned char*) p);
	exit(1);
}

-[private] char *make_str(char *b,char *e) {
	char *R;
	R= malloc(e-b+1);
	memcpy(R,b, e-b);
	R[e-b]= 0;
	return R;
}

-[ headers, begin ]
	!stdlib !stdio  !string
	!stdarg extlibs
-[ end ]


-[public] linenumber_t *linenumber_new(char *fn,int line)
{
	linenumber_t *L;
	-[ allocate object L ]
	L->file= fn;
	L->line= line;
	L->prev= 0;
	return L;
}

-[ unused symbol utokenbuffer_collectd ]

-[public] typedef struct uterm uterm_t;

-[ utermlist_t : public list of uterm_t* ]

-[public] enum uterm_kind {
	ut_number, ut_strlit, ut_word,
	ut_macroarg, ut_nmacroarg, ut_smacroarg, ut_wmacroarg,
	ut_ro_bulk, ut_sq_bulk, ut_cu_bulk,
};

-[public] struct uterm {
	enum uterm_kind kind;
	char *data;
	utermlist_t *body;
	linenumber_t *linenumber;
};

-[ import file_read_all in private scope ]

-[public] utermlist_t *ulang_parse_file(char *fn)
{
	utermlist_t *result;
	char *buffer;
	size_t len;
	buffer= file_read_all(fn);
	len= strlen(buffer);
	result= ulang_parse_buffer(buffer,len,fn);
	free(buffer);
	return result;
}

-[public] utermlist_t *ulang_parse_buffer
	(char *buffer, size_t len,char *fn)
{
	utoken_t *tokens;
	size_t ntokens;
	utermlist_t *TL;
	int i;

	ulang_scan(buffer, len, fn, &tokens, &ntokens);

	make_utermlist(fn,&TL,tokens, tokens+ntokens, 0);

	for(i=0;i<ntokens;i++) {
		utoken_t *T= tokens+i;
		if (T->data) free(T->data);
		if (T->linenumber) free(T->linenumber);
	}
	free(tokens);
	return TL;
}

-[private] utoken_t *make_utermlist
	(char *fname,utermlist_t **target, utoken_t *begin,
	utoken_t *end, int delimiter)
{
	utermlist_t *result;
	uterm_t *T;

	result= utermlist_new();

again:
	if (begin==end) {
		if (delimiter!=0)
			exit(fprintf(stderr,"%s: parser error: "
				"unexpected EOF while in parenthesised "
				"expression\n", fname));
		goto done;
	}
	switch(begin->kind) {
		case tk_rcurly:
		case tk_rsquare:
		case tk_rround:
			if (delimiter!= begin->kind)
				exit(fprintf(stderr,"%s:%d: parser error: "
				 "unmatched %s\n", begin->linenumber->file,
				begin->linenumber->line, begin->data));
			begin++;
			goto done;
		case  tk_number:
		case tk_strlit:
		case tk_word:
		case tk_macroarg:
		case tk_nmacroarg:
		case tk_smacroarg:
		case tk_wmacroarg:
		case tk_stop:
			utermlist_append(result,
					simple_token_to_term(begin));
			begin++;
			goto again;
		case tk_lcurly:
		case tk_lsquare:
		case tk_lround:
			-[ allocate object T ]
			T->kind= token_brace_to_term_brace(begin->kind);
			T->linenumber= begin->linenumber;
			begin->linenumber= 0;
			begin= make_utermlist(fname, &T->body,
				begin+1, end, begin->kind + 1);
			utermlist_append(result, T);
			goto again;
		default:
			goto again;
	}

done:
	*target= result;
	return begin;
}


-[private] uterm_t *simple_token_to_term(utoken_t *K)
{
	uterm_t *T;
	-[ allocate object T ]
	switch(K->kind) {
	case tk_number: T->kind= ut_number; break;
	case tk_strlit: T->kind= ut_strlit; break;
	case tk_word: T->kind= ut_word; break;
	case tk_macroarg: T->kind= ut_macroarg; break;
	case tk_smacroarg: T->kind= ut_smacroarg; break;
	case tk_nmacroarg: T->kind= ut_nmacroarg; break;
	case tk_wmacroarg: T->kind= ut_wmacroarg; break;
	case tk_stop:
		T->kind= ut_word;
		free(K->data);
		K->data= strdup("_semicolon");
		break;
	default:
		-[ unreachable ]
	}
	switch(T->kind) {
	case ut_macroarg:
		T->data= strdup(K->data+1);
		free(K->data);
		break;
	case ut_nmacroarg: 
	case ut_smacroarg: 
	case ut_wmacroarg: 
		T->data= strdup(strchr(K->data,':')+1);
		free(K->data);
		break;
	case ut_strlit:
		T->data= unquote_c_literal_izoz(K->data);
		free(K->data);
		break;
	default:
		T->data= K->data;
	}
	K->data= 0;
	T->linenumber= K->linenumber;
	K->linenumber= 0;
	return T;
}

-[private] enum uterm_kind token_brace_to_term_brace
	(enum utoken_kind kind)
{
	switch(kind) {
	case tk_lsquare: return ut_sq_bulk;
	case tk_lround: return ut_ro_bulk;
	case tk_lcurly: return ut_cu_bulk;
	default:
		-[ unreachable ]
	}

	return ut_number;
}

static int spaces;

-[private] void print_spaces() {
	int i;
	return ;
	for(i=0;i<spaces;i++)
		printf(" ");
}

-[public] void dump_termlist(utermlist_t *L) {
/*	print_spaces();*/
/*	printf("TERMLIST{");*/
	-[ list>for each T in *L, begin]
		dump_term(T);
	-[ end ]
/*	printf("}");*/
}

-[public] void dump_term(uterm_t *T) {
	switch(T->kind) {
	case ut_number:
	case ut_strlit:
	case ut_word:
		printf(" %s ",T->data);
		return ;
	case ut_nmacroarg: printf("$N:%s",T->data); return;
	case ut_smacroarg: printf("$S:%s",T->data); return;
	case ut_wmacroarg: printf("$W:%s",T->data); return;
	case ut_macroarg: printf("$%s",T->data); return;
	case ut_ro_bulk: printf("("); break;
	case ut_cu_bulk: printf("{"); break;
	case ut_sq_bulk: printf("["); break;
	}
	spaces+= 3;
	dump_termlist(T->body);
	spaces-= 3;
	print_spaces();
	switch(T->kind) {
	case ut_ro_bulk: printf(") "); break;
	case ut_cu_bulk: printf("} "); break;
	case ut_sq_bulk: printf("] "); break;
	default:
		break;
	}
}

